<!DOCTYPE html>
<html lang="en">

<head>
	<title>three.js webgl - geometry - text</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<link type="text/css" rel="stylesheet" href="./css/main.css">
</head>

<body>

	<script type="importmap">
			{
				"imports": {
					"three": "https://unpkg.com/three@0.162.0/build/three.module.js",
					"three/addons/": "https://unpkg.com/three@0.162.0/examples/jsm/"
				}
			}
	</script>


	<script type="module">

		import * as THREE from 'three';

		import { FontLoader } from 'three/addons/loaders/FontLoader.js';
		import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

		//  是否缓存
		// THREE.Cache.enabled = true;

		let container;

		let camera, cameraTarget, scene, renderer;

		let group, textMesh1, textMesh2, textGeo, materials;

		let firstLetter = true;

		let text = 'NHB',

			bevelEnabled = true,

			font = undefined,

			fontName = 'optimer', // helvetiker, optimer, gentilis, droid sans, droid serif
			fontWeight = 'bold'; // normal bold

		const height = 20,
			size = 70,
			hover = 30,

			curveSegments = 4,

			bevelThickness = 2,
			bevelSize = 1.5;

		const mirror = true;

		const fontMap = {

			'helvetiker': 0,
			'optimer': 1,
			'gentilis': 2,
			'droid/droid_sans': 3,
			'droid/droid_serif': 4

		};

		const weightMap = {

			'regular': 0,
			'bold': 1

		};

		const reverseFontMap = [];
		const reverseWeightMap = [];

		for (const i in fontMap) reverseFontMap[fontMap[i]] = i;
		for (const i in weightMap) reverseWeightMap[weightMap[i]] = i;

		let targetRotation = 0;
		let targetRotationOnPointerDown = 0;

		let pointerX = 0;
		let pointerXOnPointerDown = 0;

		let windowHalfX = window.innerWidth / 2;

		let fontIndex = 1;

		init();
		animate();

		function init() {

			container = document.createElement('div');
			document.body.appendChild(container);

			// CAMERA

			camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 1500);
			camera.position.set(0, 400, 700);

			cameraTarget = new THREE.Vector3(0, 150, 0);

			// SCENE

			scene = new THREE.Scene();
			scene.background = new THREE.Color(0x000000);
			scene.fog = new THREE.Fog(0x000000, 250, 1400);

			// LIGHTS

			const dirLight = new THREE.DirectionalLight(0xffffff, 0.4);
			dirLight.position.set(0, 0, 1).normalize();
			scene.add(dirLight);

			const pointLight = new THREE.PointLight(0xffffff, 4.5, 0, 0);
			pointLight.color.setHSL(Math.random(), 1, 0.5);
			pointLight.position.set(0, 100, 90);
			scene.add(pointLight);

			materials = [
				new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }), // front
				new THREE.MeshPhongMaterial({ color: 0xffffff }) // side
			];

			group = new THREE.Group();
			group.position.y = 100;

			scene.add(group);

			loadFont();

			const plane = new THREE.Mesh(
				new THREE.PlaneGeometry(10000, 10000),
				new THREE.MeshBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true })
			);
			plane.position.y = 100;
			plane.rotation.x = - Math.PI / 2;
			scene.add(plane);

			// RENDERER

			renderer = new THREE.WebGLRenderer({ antialias: true });
			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(window.innerWidth, window.innerHeight);
			container.appendChild(renderer.domElement);

			// EVENTS

			container.style.touchAction = 'none';
			container.addEventListener('pointerdown', onPointerDown);

			document.addEventListener('keypress', onDocumentKeyPress);
			document.addEventListener('keydown', onDocumentKeyDown);

			//

			const params = {
				changeColor: function () {

					pointLight.color.setHSL(Math.random(), 1, 0.5);

				},
				changeFont: function () {

					fontIndex++;

					fontName = reverseFontMap[fontIndex % reverseFontMap.length];

					loadFont();

				},
				changeWeight: function () {

					if (fontWeight === 'bold') {

						fontWeight = 'regular';

					} else {

						fontWeight = 'bold';

					}

					loadFont();

				},
				changeBevel: function () {

					bevelEnabled = !bevelEnabled;

					refreshText();

				}
			};

			//
			window.addEventListener('resize', onWindowResize);

		}

		function onWindowResize() {

			windowHalfX = window.innerWidth / 2;

			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();

			renderer.setSize(window.innerWidth, window.innerHeight);

		}

		//

		function onDocumentKeyDown(event) {

			if (firstLetter) {

				firstLetter = false;
				text = '';

			}

			const keyCode = event.keyCode;

			// backspace

			if (keyCode == 8) {

				event.preventDefault();

				text = text.substring(0, text.length - 1);
				refreshText();

				return false;

			}

		}

		function onDocumentKeyPress(event) {

			const keyCode = event.which;

			// backspace

			if (keyCode == 8) {

				event.preventDefault();

			} else {

				const ch = String.fromCharCode(keyCode);
				text += ch;

				refreshText();

			}

		}

		function loadFont() {

			const loader = new FontLoader();
			
			loader.load('fonts/' + fontName + '_' + fontWeight + '.typeface.json', function (response) {

				font = response;

				refreshText();

			});

		}

		function createText() {

			textGeo = new TextGeometry(text, {
				font: font,
				size: size,
				height: height,
				curveSegments: curveSegments,
				bevelThickness: bevelThickness,
				bevelSize: bevelSize,
				bevelEnabled: bevelEnabled
			});
			

			textGeo.computeBoundingBox();

			const centerOffset = - 0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x);

			textMesh1 = new THREE.Mesh(textGeo, materials);

			textMesh1.position.x = centerOffset;
			textMesh1.position.y = hover;
			textMesh1.position.z = 0;

			textMesh1.rotation.x = 0;
			textMesh1.rotation.y = Math.PI * 2;

			group.add(textMesh1);

			if (mirror) {

				textMesh2 = new THREE.Mesh(textGeo, materials);

				textMesh2.position.x = centerOffset;
				textMesh2.position.y = - hover;
				textMesh2.position.z = height;

				textMesh2.rotation.x = Math.PI;
				textMesh2.rotation.y = Math.PI * 2;

				group.add(textMesh2);

			}

		}

		function refreshText() {

			group.remove(textMesh1);
			if (mirror) group.remove(textMesh2);

			if (!text) return;

			createText();

		}

		function onPointerDown(event) {

			if (event.isPrimary === false) return;

			pointerXOnPointerDown = event.clientX - windowHalfX;
			targetRotationOnPointerDown = targetRotation;

			document.addEventListener('pointermove', onPointerMove);
			document.addEventListener('pointerup', onPointerUp);

		}

		function onPointerMove(event) {

			if (event.isPrimary === false) return;

			pointerX = event.clientX - windowHalfX;

			targetRotation = targetRotationOnPointerDown + (pointerX - pointerXOnPointerDown) * 0.02;

		}

		function onPointerUp() {

			if (event.isPrimary === false) return;

			document.removeEventListener('pointermove', onPointerMove);
			document.removeEventListener('pointerup', onPointerUp);

		}

		//

		function animate() {

			requestAnimationFrame(animate);

			render();

		}

		function render() {

			group.rotation.y += (targetRotation - group.rotation.y) * 0.05;
            // console.log(1111,cameraTarget);
			camera.lookAt(cameraTarget);

			renderer.clear();
			renderer.render(scene, camera);

		}

	</script>

</body>

</html>